home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / Macintosh Sample Code / SC.014.CPlusTESample / TEDocument.cp < prev    next >
Encoding:
Text File  |  1990-04-30  |  22.2 KB  |  850 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------------------
  2.  
  3.     Program:    CPlusTESample 2.0
  4.     File:        TEDocument.cp
  5.     Uses:       TEDocument.h
  6.                 TESample.h
  7.  
  8.     by Andrew Shebanow
  9.     of Apple Macintosh Developer Technical Support
  10.  
  11.     Copyright © 1989-1990 Apple Computer, Inc.
  12.     All rights reserved.
  13.  
  14. ------------------------------------------------------------------------------------------*/
  15.  
  16. // Mac Includes
  17. #ifndef __TYPES__
  18. #include <Types.h>
  19. #endif
  20. #ifndef __QUICKDRAW__
  21. #include <QuickDraw.h>
  22. #endif
  23. #ifndef __FONTS__
  24. #include <Fonts.h>
  25. #endif
  26. #ifndef __EVENTS__
  27. #include <Events.h>
  28. #endif
  29. #ifndef __CONTROLS__
  30. #include <Controls.h>
  31. #endif
  32. #ifndef __WINDOWS__
  33. #include <Windows.h>
  34. #endif
  35. #ifndef __MENUS__
  36. #include <Menus.h>
  37. #endif
  38. #ifndef __TEXTEDIT__
  39. #include <TextEdit.h>
  40. #endif
  41. #ifndef __DIALOGS__
  42. #include <Dialogs.h>
  43. #endif
  44. #ifndef __DESK__
  45. #include <Desk.h>
  46. #endif
  47. #ifndef __SCRAP__
  48. #include <Scrap.h>
  49. #endif
  50. #ifndef __TOOLUTILS__
  51. #include <ToolUtils.h>
  52. #endif
  53. #ifndef __MEMORY__
  54. #include <Memory.h>
  55. #endif
  56. #ifndef __SEGLOAD__
  57. #include <SegLoad.h>
  58. #endif
  59. #ifndef __FILES__
  60. #include <Files.h>
  61. #endif
  62. #ifndef __OSUTILS__
  63. #include <OSUtils.h>
  64. #endif
  65. #ifndef __TRAPS__
  66. #include <Traps.h>
  67. #endif
  68. #ifndef __PACKAGES__
  69. #include <Packages.h>
  70. #endif
  71. #ifndef __ERRORS__
  72. #include <Errors.h>
  73. #endif
  74.  
  75. #ifndef __TEDOCUMENT__
  76. #include "TEDocument.h"
  77. #endif
  78. #ifndef __TESAMPLE__
  79. #include "TESample.h"
  80. #endif
  81.  
  82. extern "C" {
  83.     // prototypes for functions that can't belong to TEDocument, but
  84.     // which are closely tied into our documents
  85.     pascal ClikLoopProcPtr GetOldClikLoop();
  86.     pascal void PascalClikLoop();
  87.     void CommonAction(ControlHandle control,short* amount);
  88.     pascal void VActionProc(ControlHandle control,short part);
  89.     pascal void HActionProc(ControlHandle control,short part);
  90.     // this routine is written in Assembler, since it needs to tweak registers
  91.     pascal void ASMCLIKLOOP();
  92. };
  93.  
  94. // kTextMargin is the number of pixels we leave blank at the edge of the window.
  95. const short kTextMargin = 2;
  96.  
  97. // kMaxDocWidth is an arbitrary number used to specify the width of the TERec's
  98. // destination rectangle so that word wrap and horizontal scrolling can be
  99. // demonstrated.
  100. const short    kMaxDocWidth = 576;
  101.  
  102. // kMinDocDim is used to limit the minimum dimension of a window when GrowWindow
  103. // is called.
  104. const short    kMinDocDim = 64;
  105.  
  106. // kMaxTELength is an arbitrary number used to limit the length of text in the TERec
  107. // so that various errors won't occur from too many characters being in the text.
  108. const short    kMaxTELength = 32000;
  109.  
  110. // kControlInvisible is used the same way to 'turn on' the control.
  111. const short kControlVisible = 0xFF;
  112.  
  113. // ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
  114. // values for control positioning and sizing.
  115. const short kScrollbarAdjust = 15;
  116. const short kGrowboxAdjust = 15;
  117. const short kScrollbarWidth = 16;
  118.  
  119. // kTESlop provides some extra security when pre-flighting edit commands.
  120. const short kTESlop = 1024;
  121.  
  122. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  123. // to have borders coincide with the growbox.
  124. const short kScrollTweek = 2;
  125.  
  126. // kCrChar is used to match with a carriage return when calculating the
  127. // number of lines in the TextEdit record. kDelChar is used to check for
  128. // delete in keyDowns.
  129. const short kCrChar = 13;
  130. const short kDelChar = 8;
  131.  
  132. // notice that we pass the resID parameter up to our base class,
  133. // which actually creates the window for us
  134. TEDocument::TEDocument(short resID)    : TDocument(resID, kTEFileType)
  135. {
  136.     Boolean good;
  137.     Rect destRect, viewRect;
  138.  
  139.     good = false;
  140.     SetPort(fDocWindow);
  141.     GetTERect(&viewRect);
  142.     destRect = viewRect;
  143.     destRect.right = destRect.left + kMaxDocWidth;
  144.     fDocTE = TENew(&destRect, &viewRect);
  145.     FailNIL(fDocTE);
  146.  
  147.     // set up TE record
  148.     AdjustViewRect();
  149.     TEAutoView(true, fDocTE);
  150.     fDocClik = (*fDocTE)->clikLoop;
  151.     (*fDocTE)->clikLoop = (ClikLoopProcPtr) ASMCLIKLOOP;
  152.  
  153.     fDocVScroll = fDocHScroll = nil;
  154.  
  155.     // get vertical scrollbar
  156.     TRY
  157.       {
  158.         fDocVScroll = GetNewControl(rVScroll, fDocWindow);
  159.         FailNILResource(fDocVScroll);
  160.         fDocHScroll = GetNewControl(rHScroll, fDocWindow);
  161.         FailNILResource(fDocHScroll);
  162.       }
  163.     RECOVER
  164.       {
  165.         TEDispose(fDocTE);
  166.         if (fDocVScroll)
  167.           delete fDocVScroll;
  168.         if (fDocHScroll)
  169.           delete fDocHScroll;
  170.         FailNewMessage(eNoWindow,gFailMessage,kTEDocErrStrings);
  171.       }
  172.     ENDTRY
  173.  
  174.     AdjustScrollValues(true);
  175.     ShowWindow(fDocWindow);
  176.     SelectWindow(fDocWindow);
  177. }
  178.  
  179. TEDocument::~TEDocument()
  180. {
  181.     HideWindow(fDocWindow);
  182.     if ( fDocTE != nil )
  183.       {
  184.         TEDispose(fDocTE);            // dispose the TEHandle if we got far enough to make one
  185.       }
  186.     if ( fDocVScroll != nil )
  187.       {
  188.         DisposeControl(fDocVScroll);
  189.       }
  190.     if ( fDocHScroll != nil )
  191.       {
  192.         DisposeControl(fDocHScroll);
  193.       }
  194.     // base class destructor will dispose of window
  195. }
  196.  
  197. void TEDocument::DoZoom(short partCode)
  198. {
  199.     Rect tRect;
  200.  
  201.     tRect = fDocWindow->portRect;
  202.     EraseRect(&tRect);
  203.     ZoomWindow(fDocWindow, partCode, fDocWindow == FrontWindow());
  204.     AdjustScrollbars(true);        // adjust, redraw anyway
  205.     AdjustTE();
  206.     InvalRect(&tRect);            // invalidate the whole content
  207.     // the scrollbars were taken care of by AdjustScrollbars, so validate ’em
  208.     tRect = (*fDocVScroll)->contrlRect;
  209.     ValidRect(&tRect);
  210.     tRect = (*fDocHScroll)->contrlRect;
  211.     ValidRect(&tRect);
  212. }
  213.  
  214. // Called when a mouseDown occurs in the grow box of an active window.
  215.  
  216. void TEDocument::DoGrow(EventRecord* theEvent)
  217. {
  218.     long growResult;
  219.     Rect tRect, tRect2;
  220.  
  221.     tRect = qd.screenBits.bounds;
  222.     tRect.left = kMinDocDim;
  223.     tRect.top = kMinDocDim;
  224.     growResult = GrowWindow(fDocWindow, theEvent->where, &tRect);
  225.     // see if it really changed size
  226.     if ( growResult != 0 )
  227.       {
  228.         tRect = (*fDocTE)->viewRect;
  229.         SizeWindow(fDocWindow, LoWord(growResult), HiWord(growResult), true);
  230.         AdjustScrollbars(true);
  231.         AdjustTE();
  232.         // calculate & validate the region that hasn’t changed so it won’t get redrawn
  233.         // Note: we copy rectangles so that we don't take address of object fields.
  234.         tRect2 = (*fDocTE)->viewRect;
  235.         (void) SectRect(&tRect, &tRect2, &tRect);
  236.         tRect2 = fDocWindow->portRect; InvalRect(&tRect2);
  237.         ValidRect(&tRect);
  238.         tRect2 = (*fDocVScroll)->contrlRect; ValidRect(&tRect2);
  239.         tRect2 = (*fDocHScroll)->contrlRect; ValidRect(&tRect2);
  240.       }
  241. }
  242.  
  243. void TEDocument::DoContent(EventRecord* theEvent)
  244. {
  245.     Point mouse;
  246.     ControlHandle control;
  247.     short part, value;
  248.     Boolean shiftDown;
  249.     Rect teRect;
  250.  
  251.     SetPort(fDocWindow);
  252.     mouse = theEvent->where;                            // get the click position
  253.     GlobalToLocal(&mouse);
  254.     GetTERect(&teRect);
  255.     if ( PtInRect(mouse, &teRect) )
  256.       {
  257.         // see if we need to extend the selection
  258.         shiftDown = (theEvent->modifiers & shiftKey) != 0;    // extend if Shift is down
  259.         TEClick(mouse, shiftDown, fDocTE);
  260.       }
  261.     else
  262.       {
  263.         part = FindControl(mouse, fDocWindow, &control);
  264.         switch ( part )
  265.           {
  266.             case 0:
  267.                 // do nothing if not in a control
  268.                 break;
  269.             case inThumb:
  270.                 value = GetCtlValue(control);
  271.                 part = TrackControl(control, mouse, nil);
  272.                 if ( part != 0 )
  273.                   {
  274.                     value -= GetCtlValue(control);
  275.                     // value now has CHANGE in value; if value changed, scroll
  276.                     if ( value != 0 )
  277.                         if ( control == fDocVScroll )
  278.                             TEScroll(0, value * (*fDocTE)->lineHeight, fDocTE);
  279.                         else TEScroll(value, 0, fDocTE);
  280.                   }
  281.                 break;
  282.             default:                        // they clicked in an arrow, so track & scroll
  283.                 if ( control == fDocVScroll )
  284.                     value = TrackControl(control, mouse, (ProcPtr) VActionProc);
  285.                 else value = TrackControl(control, mouse, (ProcPtr) HActionProc);
  286.                 break;
  287.           }
  288.       }
  289. }
  290.  
  291. void TEDocument::DoKeyDown(EventRecord* theEvent)
  292. {
  293.     char key;
  294.  
  295.     if (theEvent->modifiers & cmdKey)    // don't process command characters
  296.       return;
  297.     key = (char) (theEvent->message & charCodeMask);
  298.     // we have a char. for our window; see if we are still below TextEdit’s
  299.     // limit for the number of characters
  300.     if ((key != kDelChar) &&
  301.         ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart) + 1 >= kMaxTELength) )
  302.       Failure(eExceedChar,kTEDocErrStrings);
  303.  
  304.     TEKey(key, fDocTE);
  305.     AdjustScrollbars(false);
  306.     AdjustTE();
  307.     fDirty = true;
  308. }
  309.  
  310. void TEDocument::DoActivate(Boolean becomingActive)
  311. {
  312.     if ( becomingActive )
  313.       {
  314.         RgnHandle    tempRgn;
  315.         RgnHandle    clipRgn;
  316.         Rect        growRect;
  317.         Rect        tRect;
  318.  
  319.         // since we don’t want TEActivate to draw a selection in an area where
  320.         // we’re going to erase and redraw, we’ll clip out the update region
  321.         // before calling it.
  322.         tempRgn = NewRgn();
  323.         clipRgn = NewRgn();
  324.         // save old update region
  325.         CopyRgn(((WindowPeek) fDocWindow)->updateRgn, tempRgn);
  326.         // put it in local coords
  327.         OffsetRgn(tempRgn, fDocWindow->portBits.bounds.left, fDocWindow->portBits.bounds.top);
  328.         GetClip(clipRgn);
  329.         // subtract updateRgn from clipRgn
  330.         DiffRgn(clipRgn, tempRgn, tempRgn);
  331.         // make it the new clipRgn
  332.         SetClip(tempRgn);
  333.         TEActivate(fDocTE);
  334.         // restore the full-blown clipRgn
  335.         SetClip(clipRgn);
  336.         // get rid of temp regions
  337.         DisposeRgn(tempRgn);
  338.         DisposeRgn(clipRgn);
  339.  
  340.         // the controls must be redrawn on activation:
  341.         (*fDocVScroll)->contrlVis = kControlVisible;
  342.         (*fDocHScroll)->contrlVis = kControlVisible;
  343.         // copy rectangles to avoid unsafe object field references!
  344.         tRect = (*fDocVScroll)->contrlRect; InvalRect(&tRect);
  345.         tRect = (*fDocHScroll)->contrlRect; InvalRect(&tRect);
  346.         // the growbox needs to be redrawn on activation:
  347.         growRect = fDocWindow->portRect;
  348.         // adjust for the scrollbars
  349.         growRect.top = growRect.bottom - kScrollbarAdjust;
  350.         growRect.left = growRect.right - kScrollbarAdjust;
  351.         InvalRect(&growRect);
  352.       }
  353.     else
  354.       {
  355.         TEDeactivate(fDocTE);
  356.         // the controls must be hidden on deactivation:
  357.         HideControl(fDocVScroll);
  358.         HideControl(fDocHScroll);
  359.         // we draw grow icon immediately, since we deactivate controls
  360.         // immediately, and the update delay looks funny
  361.         DrawGrowIcon(fDocWindow);
  362.       }
  363. }
  364.  
  365. void TEDocument::DoUpdate()
  366. {
  367.     BeginUpdate(fDocWindow);                // this sets up the visRgn
  368.     if ( ! EmptyRgn(fDocWindow->visRgn) )    // draw if updating needs to be done
  369.       {
  370.         DrawWindow();
  371.       }
  372.     EndUpdate(fDocWindow);
  373. }
  374.  
  375. // calculate how much idle time we need
  376.  
  377. unsigned long TEDocument::CalcIdle()
  378. {
  379.     if (HaveSelection())
  380.       return GetCaretTime();
  381.     else return kMaxSleepTime;    // if we don't have a selection, we don't need to idle
  382. }
  383.  
  384. // This is called whenever we get a null event et al.
  385. // It takes care of necessary periodic actions. For this program,
  386. // it calls TEIdle.
  387.  
  388. void TEDocument::DoIdle()
  389. {
  390.     TEIdle(fDocTE);
  391. } // DoIdle
  392.  
  393. // Draw the contents of an application window.
  394.  
  395. void TEDocument::DrawWindow()
  396. {
  397.     Rect tRect;
  398.  
  399.     SetPort(fDocWindow);
  400.     tRect = fDocWindow->portRect;
  401.     EraseRect(&tRect);
  402.     TEUpdate(&tRect, fDocTE);
  403.     DrawControls(fDocWindow);
  404.     DrawGrowIcon(fDocWindow);
  405. } // DrawWindow
  406.  
  407. // Return a rectangle that is inset from the portRect by the size of
  408. // the scrollbars and a little extra margin.
  409.  
  410. void TEDocument::GetTERect(Rect* teRect)
  411. {
  412.     *teRect = fDocWindow->portRect;
  413.     InsetRect(teRect, kTextMargin, kTextMargin);            // adjust for margin
  414.     teRect->bottom = teRect->bottom - kScrollbarAdjust;    // and for the scrollbars
  415.     teRect->right = teRect->right - kScrollbarAdjust;
  416. } // GetTERect
  417.  
  418. // setup a region which contains the visible text
  419.  
  420. void TEDocument::GetVisTERgn(RgnHandle rgn)
  421. {
  422.     Rect teRect;
  423.  
  424.     teRect = (*fDocTE)->viewRect;    // get a local copy of viewRect
  425.     SetPort(fDocWindow);            // make sure we have right port
  426.     LocalToGlobal((Point*) &teRect.top);
  427.     LocalToGlobal((Point*) &teRect.bottom);
  428.     RectRgn(rgn, &teRect);
  429.     // we temporarily change the port’s origin to “globalfy” the visRgn
  430.     SetOrigin(-(fDocWindow->portBits.bounds.left),
  431.               -(fDocWindow->portBits.bounds.top));
  432.     SectRgn(rgn, fDocWindow->visRgn, rgn);
  433.     SetOrigin(0, 0);
  434. } // GetTERgn
  435.  
  436. void TEDocument::ReadFromFile(short refNum)
  437. {
  438.     long curPos, size;
  439.     Handle h;
  440.  
  441.     // determine how much data is available to read
  442.     FailOSErr(GetFPos(refNum,&curPos));
  443.     FailOSErr(GetEOF(refNum,&size));
  444.     size -= curPos;
  445.  
  446.     // check for size > 32K
  447.     if (size > kMaxTELength)
  448.       Failure(eExceedChar,kTEDocErrStrings);
  449.  
  450.     // allocate a handle to store it in
  451.     h = NewHandle(size);
  452.     FailNIL(h);
  453.  
  454.     TRY
  455.       {
  456.         FailOSErr(FSRead(refNum,&size,*h));
  457.       }
  458.     RECOVER
  459.       {
  460.         DisposHandle(h);
  461.       }
  462.     ENDTRY
  463.  
  464.     // now make the text the current text
  465.     SetPort(fDocWindow);
  466.     HLock(h);
  467.     TESetText(*h,size,fDocTE);
  468.     DisposHandle(h);
  469.  
  470.     // make sure everything is up to date
  471.     Rect tRect = (*fDocTE)->viewRect;
  472.     InvalRect(&tRect);
  473.     AdjustTE();
  474.     AdjustScrollbars(false);
  475. }
  476.  
  477. void TEDocument::WriteToFile(short refNum)
  478. {
  479.     Handle h;
  480.     long size;
  481.  
  482.     // get COPY of TEHandle - doesn't alloc memory
  483.     h = (Handle) TEGetText(fDocTE);
  484.     size = GetHandleSize(h);
  485.     FailOSErr(FSWrite(refNum,&size,*h));
  486. }
  487.  
  488. // Return boolean value indicating that there is or is not a
  489. // selection in the document
  490.  
  491. Boolean TEDocument::HaveSelection()
  492. {
  493.     if ( (*fDocTE)->selStart < (*fDocTE)->selEnd )
  494.       return true;
  495.     else return false;
  496. }
  497.  
  498. // Update the TERec's view rect so that it is the greatest multiple of
  499. // the lineHeight that still fits in the old viewRect.
  500.  
  501. void TEDocument::AdjustViewRect()
  502. {
  503.     TEPtr te;
  504.  
  505.     te = *fDocTE;
  506.     te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
  507.                             * te->lineHeight) + te->viewRect.top;
  508. } // AdjustViewRect
  509.  
  510. // Scroll the TERec around to match up to the potentially updated scrollbar
  511. // values. This is really useful when the window has been resized such that the
  512. // scrollbars became inactive but the TERec was already scrolled.
  513.  
  514. void TEDocument::AdjustTE()
  515. {
  516.     TEPtr te;
  517.  
  518.     te = *fDocTE;
  519.     TEScroll((te->viewRect.left - te->destRect.left) - GetCtlValue(fDocHScroll),
  520.              (te->viewRect.top - te->destRect.top) -
  521.                  (GetCtlValue(fDocVScroll) * te->lineHeight),
  522.              fDocTE);
  523. } // AdjustTE
  524.  
  525. // Re-calculate the position and size of the viewRect and the scrollbars.
  526. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  527. // to have borders coincide with the growbox.
  528.  
  529. void TEDocument::AdjustScrollSizes()
  530. {
  531.     Rect teRect;
  532.  
  533.     GetTERect(&teRect);
  534.     (*fDocTE)->viewRect = teRect;
  535.     AdjustViewRect();
  536.     MoveControl(fDocVScroll, fDocWindow->portRect.right - kScrollbarAdjust, -1);
  537.     SizeControl(fDocVScroll, kScrollbarWidth,
  538.                 fDocWindow->portRect.bottom - fDocWindow->portRect.top -
  539.                     kGrowboxAdjust + kScrollTweek);
  540.     MoveControl(fDocHScroll, -1, fDocWindow->portRect.bottom - kScrollbarAdjust);
  541.     SizeControl(fDocHScroll,
  542.                 fDocWindow->portRect.right - fDocWindow->portRect.left -
  543.                     kGrowboxAdjust + kScrollTweek,
  544.                 kScrollbarWidth);
  545. } // AdjustScrollSizes
  546.  
  547. // Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
  548. // and we don't want that). If the controls are to be resized as well, call the procedure to do that,
  549. // then call the procedure to adjust the maximum and current values. Finally re-enable the controls
  550. // by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be
  551. // necessary).
  552.  
  553. void TEDocument::AdjustScrollbars(Boolean needsResize)
  554. {
  555.     // First, turn visibility of scrollbars off so we won’t get unwanted redrawing
  556.     (*fDocVScroll)->contrlVis = 0;
  557.     (*fDocHScroll)->contrlVis = 0;
  558.     if ( needsResize )
  559.       AdjustScrollSizes();
  560.     AdjustScrollValues(needsResize);
  561.     // Now, restore visibility in case we never had to draw during adjustment
  562.     (*fDocVScroll)->contrlVis = 0xff;
  563.     (*fDocHScroll)->contrlVis = 0xff;
  564. } // AdjustScrollbars
  565.  
  566. // Calculate the new control maximum value and current value, whether it is the horizontal or
  567. // vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
  568. // vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
  569. // width to the width of the viewRect. The current values are set by comparing the offset between
  570. // the view and destination rects. If necessary, redraw the control by calling ShowControl.
  571.  
  572. void TEDocument::AdjustHV(Boolean isVert,Boolean mustRedraw)
  573. {
  574.     short value, lines, max;
  575.     short oldValue, oldMax;
  576.     TEPtr te;
  577.     ControlHandle control;
  578.  
  579.     if (isVert)
  580.       control = fDocVScroll;
  581.     else control = fDocHScroll;
  582.     oldValue = GetCtlValue(control);
  583.     oldMax = GetCtlMax(control);
  584.     te = *fDocTE;                            // point to TERec for convenience
  585.     if ( isVert )
  586.       {
  587.         lines = te->nLines;
  588.         // since nLines isn’t right if the last character is a return, check for that case
  589.         if ( *(*te->hText + te->teLength - 1) == kCrChar )
  590.           lines += 1;
  591.         max = lines - ((te->viewRect.bottom - te->viewRect.top) /
  592.                        te->lineHeight);
  593.       }
  594.     else max = kMaxDocWidth - (te->viewRect.right - te->viewRect.left);
  595.  
  596.     if ( max < 0 )
  597.       max = 0;
  598.     SetCtlMax(control, max);
  599.  
  600.     // Must deref. after SetCtlMax since, technically, it could draw and therefore move
  601.     // memory. This is why we don’t just do it once at the beginning.
  602.     te = *fDocTE;
  603.     if ( isVert )
  604.       value = (te->viewRect.top - te->destRect.top) / te->lineHeight;
  605.     else value = te->viewRect.left - te->destRect.left;
  606.  
  607.     if ( value < 0 )
  608.       value = 0;
  609.     else if ( value >  max )
  610.       value = max;
  611.  
  612.     SetCtlValue(control, value);
  613.     // now redraw the control if asked to or if a setting changed
  614.     if ( mustRedraw || (max != oldMax) || (value != oldValue) )
  615.         ShowControl(control);
  616. } // AdjustHV
  617.  
  618. // Simply call the common adjust routine for the vertical and horizontal scrollbars.
  619.  
  620. void TEDocument::AdjustScrollValues(Boolean mustRedraw)
  621. {
  622.     AdjustHV(true, mustRedraw);
  623.     AdjustHV(false, mustRedraw);
  624. } // AdjustScrollValues
  625.  
  626. ClikLoopProcPtr TEDocument::GetClikLoop()
  627. {
  628.     return fDocClik;
  629. }
  630.  
  631. TEHandle TEDocument::GetTEHandle()
  632. {
  633.     return fDocTE;
  634. }
  635.  
  636. void TEDocument::DoCut()
  637. {
  638.     long total, contig;
  639.  
  640.     if (ZeroScrap() == noErr)
  641.       {
  642.         PurgeSpace(&total, &contig);
  643.         if ((*fDocTE)->selEnd - (*fDocTE)->selStart + kTESlop > contig)
  644.           Failure(eNoSpaceCut,kTEDocErrStrings);
  645.  
  646.         TECut(fDocTE);
  647.         fDirty = true;
  648.         if (TEToScrap() != noErr)
  649.           {
  650.             (void) ZeroScrap();
  651.             Failure(eNoCut,kTEDocErrStrings);
  652.           }
  653.       }
  654.     AdjustScrollbars(false);
  655.     AdjustTE();
  656. }
  657.  
  658. void TEDocument::DoCopy()
  659. {
  660.     if (ZeroScrap() == noErr)
  661.       {
  662.         TECopy(fDocTE);                // after copying, export the TE scrap
  663.         if (TEToScrap() != noErr)
  664.           {
  665.             ZeroScrap();
  666.             Failure(eNoCopy,kTEDocErrStrings);
  667.           }
  668.       }
  669.     AdjustScrollbars(false);
  670.     AdjustTE();
  671. }
  672.  
  673. void TEDocument::DoPaste()
  674. {
  675.     Handle aHandle;
  676.     long oldSize, newSize;
  677.     OSErr saveErr;
  678.  
  679.     if (TEFromScrap() != noErr)
  680.       Failure(eNoPaste,kTEDocErrStrings);
  681.  
  682.     if ( TEGetScrapLen() + ((*fDocTE)->teLength -
  683.          ((*fDocTE)->selEnd - (*fDocTE)->selStart)) > kMaxTELength )
  684.       Failure(eExceedPaste,kTEDocErrStrings);
  685.  
  686.     aHandle = (Handle) TEGetText(fDocTE);
  687.     oldSize = GetHandleSize(aHandle);
  688.     newSize = oldSize + TEGetScrapLen() + kTESlop;
  689.  
  690.     // preflight the growth of the text handle for textedit,
  691.     // since it will crash if it doesn't have enough memory
  692.     SetHandleSize(aHandle, newSize);
  693.     saveErr = MemError();
  694.     SetHandleSize(aHandle, oldSize);
  695.     if (saveErr != noErr)
  696.       Failure(eNoSpacePaste,kTEDocErrStrings);
  697.  
  698.     TEPaste(fDocTE);
  699.     fDirty = true;
  700.  
  701.     AdjustScrollbars(false);
  702.     AdjustTE();
  703. }
  704.  
  705. void TEDocument::DoClear()
  706. {
  707.     TEDelete(fDocTE);
  708.     fDirty = true;
  709.     AdjustScrollbars(false);
  710.     AdjustTE();
  711. }
  712.  
  713. void TEDocument::DoSelectAll()
  714. {
  715.     long selSize = (*fDocTE)->teLength;
  716.     TESetSelect(0,selSize,fDocTE);
  717. }
  718.  
  719. /*
  720.     Routines used by this class, which don't belong to the class since we use
  721.     them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
  722. */
  723.  
  724. // we refer back to the owning application so that we can get access to
  725. // the document list, to find the current document object.
  726. extern TESample* gTheApplication;
  727.  
  728. // Common algorithm for pinning the value of a control. It returns the actual amount
  729. // the value of the control changed.
  730.  
  731. void CommonAction(ControlHandle control,short* amount)
  732. {
  733.     short        value, max;
  734.  
  735.     value = GetCtlValue(control);
  736.     max = GetCtlMax(control);
  737.     *amount = value - *amount;
  738.     if ( *amount <= 0 )
  739.         *amount = 0;
  740.     else if ( *amount >= max )
  741.         *amount = max;
  742.     SetCtlValue(control, *amount);
  743.     *amount = value - *amount;
  744. } // CommonAction
  745.  
  746.  
  747. // Determines how much to change the value of the vertical scrollbar by and how
  748. // much to scroll the TE record.
  749.  
  750. pascal void VActionProc(ControlHandle control,short part)
  751. {
  752.     short        amount;
  753.     WindowPtr    window;
  754.     TEPtr        te;
  755.     TEDocument* doc;
  756.  
  757.     if ( part != 0 )
  758.       {
  759.         window = (*control)->contrlOwner;
  760.         doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
  761.         te = *(doc->GetTEHandle());
  762.         switch ( part )
  763.           {
  764.             case inUpButton:
  765.             case inDownButton:        // one line
  766.                 amount = 1;
  767.                 break;
  768.             case inPageUp:            // one page
  769.             case inPageDown:
  770.                 amount = (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
  771.                 break;
  772.           }
  773.         if ( (part == inDownButton) || (part == inPageDown) )
  774.             amount = -amount;        // reverse direction for a downer
  775.         CommonAction(control, &amount);
  776.         if ( amount != 0 )
  777.             TEScroll(0, amount * te->lineHeight, doc->GetTEHandle());
  778.       }
  779. } // VActionProc
  780.  
  781. // Determines how much to change the value of the horizontal scrollbar by and how
  782. // much to scroll the TE record.
  783.  
  784. pascal void HActionProc(ControlHandle control,short part)
  785. {
  786.     short        amount;
  787.     WindowPtr    window;
  788.     TEPtr        te;
  789.     TEDocument* doc;
  790.  
  791.     if ( part != 0 )
  792.       {
  793.         window = (*control)->contrlOwner;
  794.         doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
  795.         te = *(doc->GetTEHandle());
  796.         switch ( part )
  797.           {
  798.             case inUpButton:
  799.             case inDownButton:        // a few pixels
  800.                 amount = 4;
  801.                 break;
  802.             case inPageUp:            // a page
  803.             case inPageDown:
  804.                 amount = te->viewRect.right - te->viewRect.left;
  805.                 break;
  806.           }
  807.         if ( (part == inDownButton) || (part == inPageDown) )
  808.             amount = -amount;        // reverse direction
  809.         CommonAction(control, &amount);
  810.         if ( amount != 0 )
  811.             TEScroll(amount, 0, doc->GetTEHandle());
  812.       }
  813. } // VActionProc
  814.  
  815. // Gets called from our assembly language routine, AsmClikLoop, which is in
  816. // turn called by the TEClick toolbox routine. Saves the windows clip region,
  817. // sets it to the portRect, adjusts the scrollbar values to match the TE scroll
  818. // amount, then restores the clip region.
  819.  
  820. pascal void PascalClikLoop()
  821. {
  822.     RgnHandle    region;
  823.     WindowPtr wind;
  824.     TEDocument* doc;
  825.  
  826.     wind = FrontWindow();
  827.     doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(wind);
  828.     region = NewRgn();
  829.     GetClip(region);    // save clip
  830.     ClipRect(&wind->portRect);
  831.     doc->AdjustScrollValues(false);
  832.     SetClip(region);    // restore clip
  833.     DisposeRgn(region);
  834. } // PascalClikLoop
  835.  
  836. // Gets called from our assembly language routine, AsmClikLoop, which is in
  837. // turn called by the TEClick toolbox routine. It returns the address of the
  838. // default clikLoop routine that was put into the TERec by TEAutoView to
  839. // AsmClikLoop so that it can call it.
  840.  
  841. pascal ClikLoopProcPtr GetOldClikLoop()
  842. {
  843.     TEDocument* doc;
  844.  
  845.     doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(FrontWindow());
  846.     if (doc == nil)
  847.       return nil;
  848.     return doc->GetClikLoop();
  849. } // GetOldClikLoop
  850.